home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
v9n03.arc
/
CFCOMPC.ASM
< prev
next >
Wrap
Assembly Source File
|
1990-01-12
|
97KB
|
1,582 lines
PAGE 60,132
TITLE CFCOMPC - CHKfile Compressed file compare utility
; SUBTTL General program description and use of common storage
; ----------------------------------------------------------------------------;
; CFCOMPC - Compare compressed report files created by CHKfileC ;
; ----------------------------------------------------------------------------;
; CFCOMPC 1.0 ■ PCDATA TOOLKIT Copyright (c) 1990 Ziff Communications Co. ;
; PC Magazine ■ Wolfgang Stiller ;
; ;
;-----------------------------------------------------------------------------;
; Purpose: ;
; CFCOMPC does a high speed compare of report files created by CHKfileC. ;
; CFCOMPC then displays which files have been changed, deleted or added ;
; between the creation of the OLD and NEW report files. ;
; ----------------------------------------------------------------------------;
; Format: ;
; ;
; CFCOMPC OLDfile NEWfile [/C] [/O] [/P] ;
; ;
; Oldfile and NewFile are CHKFILEC.COM created report file. ;
; "/C" Changes only - reports and checks only for changed files ;
; and ignores additions or deletions from file list. ;
; "/O" Only use check fields and file size for comparison. DOS time;
; and date stamps are not used in comparing. ;
; "/P" Pauses after each page of output if non-matches are detected.
; ----------------------------------------------------------------------------;
;Remarks: ;
; ;
; CFCOMPC utilizes a high speed compare algorithm tailored specifically ;
; to comparing report files produced by CHKfileC. It will work even if ;
; the directories have been sorted in a different order. The order of ;
; the files in the NEW and OLD files is completely independent. If the ;
; order is the same, (which is usually the case) CFCOMPC runs the ;
; fastest. CFCOMPC writes to DOS standard output, so that its output ;
; may be redirected to a file. "CFCOMPC A B >OUT" will compare old file ;
; A and new file B with any differences reported on file OUT. ;
; ;
; CFCOMPC will utilize the check value 1 and 2 fields to identify ;
; certain special types of files. "Dir." will appear if the entry is a ;
; directory. If CHKfileC was unable to read or open the file, "Read ;
; fail" or "Open fail" will appear in these fields. As much other ;
; information will displayed as possible. ;
; ;
; The individual report (input) files may not be larger than 65,536 ;
; bytes. This size allows up to 2620 file entries in each report ;
; (input) file. To compare this many entries in each file, CFCOMPC ;
; requires that at least 133,000 bytes of memory are free. CFCOMPC will ;
; automatically adjust its memory allocation to use whatever memory is ;
; available. For each 1000 less bytes of free memory, 20 fewer file ;
; entries can be supported. CFCOMPC will verify the internal integrity ;
; check data in each report file and report that the file is an invalid ;
; type if corruption is detected. ;
; ;
; If the OLDfile is not found, CFCOMPC will report this fact and then ;
; continue executing, treating OLDfile as if it were an empty file. ;
; ;
; All serious error conditions (such as a corrupted or invalid report ;
; file) will result in an error message accompanied by a beep, a prompt ;
; and a wait for a key press. ;
; ;
; CFCOMPC will return the following DOS ERRORLEVELs (decimal): ;
; (Higher DOS ERRORLEVELS override the lower ones) ;
; 128 - indicates control card syntax error ;
; 20 - indicates file open or other I/O error on one of the files ;
; 16 - indicates file(s) of invalid type for CFCOMPC or file too large ;
; for available memory. ;
; 12 - indicates file(s) have been changed ;
; 8 - indicates file(s) added and/or deleted but no files changed ;
; 4 - indicates file(s) have probably been renamed rather than actually ;
; changed or deleted, since the "TOTAL==>" lines match but the files ;
; themselves did not. ;
; 0 - files match exactly ;
; ;
; -------------------------------------------------------------------------- ;
;Sample output: ;
; ;
;CFCOMPC 1.0 ■ PCDATA TOOLKIT (c) 1990 Ziff Communications Co. ;
;PC Magazine ■ Wolfgang Stiller in directory: \ASM\EXAMPLES ;
; File Name + Check Check File Update Update ;
; Extension: Val1: Val2: Size: Date: Time: ;
; ---------- ---- ---- ----- ------ ------ ;
;Chgd: OLD:SDOC.BAK BC37 2709 61A6 02/09/89 00:07:54 ;
; NEW:SDOC.BAK A6B4 A64F 6769 02/11/89 23:55:02 ;
;Chgd: OLD:SDOC BC37 2709 61A6 02/09/89 00:13:48 ;
; NEW:SDOC BC37 2709 61A6 02/12/89 00:07:54 ;
;Deleted-->OUT 1E4A 4FD5 65 02/12/89 00:10:30 ;
;NEW Fil-->NEWOUT.TXT 1E4A 4FD5 65 02/12/89 00:10:30 ;
;NEW Fil-->TESTDIR Dir. 02/11/89 00:07:48 ;
;File totals unequal ;
; ;
;Notes: CFCOMPC displays which directory it is in when doing the compare. ;
; In this case, it is in directory \ASM\EXAMPLES. Files SDOC and ;
; SDOC.BAK have both changed. Even though file OUT has apparently ;
; been deleted and file NEWOUT.TXT has been added, it is obvious ;
; that file OUT has merely been renamed since the check values and ;
; file sizes match. TESTDIR is the name of a directory which has ;
; been added. ;
; ----------------------------------------------------------------------------;
;---------------------------------------------------------------;
; Constants: ;
;---------------------------------------------------------------;
BOX EQU 254 ;Small box character code
CR EQU 0Dh
LF EQU 0Ah
CRLF EQU 0A0Dh ;Carriage return line feed.
Rep_Rec_len EQU 25 ;Length of the rep (report) record
;The following 2 values must be even:
Rep_Key_len EQU 12 ;Length of the key part of rep record
Rep_NonKEY_Len EQU 12 ;Nonkey = rec_len - (key + FT_byte)
DSP_Rec_len EQU 51 ;Length of the DSP (Display) record
Tot_Rec_len EQU 8 ;Length of final totals + check record
;---------------------------------------------------------------;
; Structures: ;
;---------------------------------------------------------------;
Rep_Rec STRUC ;Define structure of compresed
; report records
Rep_F_Type DB ? ;Indicates if its a file(1) or dir(2)
;or open err (3) or I/O error (4)
Rep_F_Name DB 12 DUP (?) ;12 spaces reserved for filename
; ** Note, CHK+XOR sums and FS MUST immediately follow the KEY fields **
Rep_CHK_Sum DW ? ;Check value 1 (CHKsum) 16 bits
Rep_XOR_Sum DW ? ;Check value 2 (XOR value) 16 bits
Rep_FS_Lowr DW ? ;File size lower part of double word
Rep_FS_HIr DW ? ;File size: upper prt of Double Word
Rep_F_Date DW ? ;Date of last file update:
Rep_F_Time DW ? ;Time of last file update
Rep_Rec ENDS
Tot_Rec STRUC ;Define structure of the final TOTALS
; record
TOT_CHK_Sum DW ? ;Total of all check value 1s (CHKSUM)
TOT_XOR_Sum DW ? ;Combination of all check value 2s
TOT_Int_XOR DW ? ;Internal chk dif of this file itself
TOT_Int_CHK DW ? ;Internal XOR check value (this file)
Tot_Rec ENDS
CSEG SEGMENT
ASSUME CS:CSEG, DS:CSEG, ES:Nothing, SS:CSEG
SUBTTL Main program
;******************************************************************************;
;** Main program begins here -CFCOMPC- **;
;******************************************************************************;
ORG 100h ; This is a COM type program
CFCOMPC:
CALL Parse_parms_Open_Files ;Parse cmdline paramters + open files
MOV BX,offset Buffer_Area+200 ;locate stack down in prog storage
MOV SP,BX ;Stack of 200 bytes
CALL Allocate_Memory ;Allocate memory for file buffers
;and release other unused memory.
;ES will point to start of allco mem
CALL Read_and_Validate_Files ;Validate that both files are
;readable and of right type to read
;DS is now addressing DATA SEG (DS=CS)
XOR BX,BX ;Zero highest error level variable(BL)
MOV BH,Page_mode ;Load line counter + pagemode flag
; =FFh for no paging
CALL Compare_Files ;Compare OLD file with NEW file and
; determine changed or deleted recs
CALL Scan_for_Additions ;Look for records left on NEW file
CALL Compare_totals ;Check if TOT_REC CHK + XOR fields =
CMP BH,0FFh ;See if page mode is off (= FFh)
JE Normal_termination ; IF turned off, skip page control
OR BL,BL ;See if changes detected (BL not = 0)
JE Normal_termination ; If no changes, then terminate
CALL Page_Wait ; otherwise pause for user to read
; the display.
Normal_termination:
MOV AL,BL ;Load error level for termination
MOV AH,4Ch ; terminate
INT 21h
;---------------------------------------------------------------------------;
; Compare files - will compare OLD and NEW file (25 character records) ;
;---------------------------------------------------------------------------;
; Register conventions: ;
; DS=Segment register for OLD SEG ES=Segment of current NEW rec ;
; SI=offset of current OLD rec DI=offset of current NEW rec ;
; CX=number of OLD recs left to proc. BP=offset of last NEW rec ;
; BL=highest code for DOS errorlevel BH=line counter (for /P option) ;
;---------------------------------------------------------------------------;
; Entry: ;
; Both files have been read into file buffers and validated ;
; DS is segment register for DATA(=CS); ES is segment regst for NEW file. ;
; Exit: ;
; Reports will be written to STD output show all file changes + additions.;
; All matched NEW file entries have 1st byte of filename zeroed. ;
; Reports have been written to Std output, on discrepancies so far. ;
; BL = 12 if changes, =8 if added/deleted files, and =0 for no changes ;
;---------------------------------------------------------------------------;
Compare_early_exit:
RET
Compare_Files:
XOR DI,DI ;1st record address of NEW file = zero
MOV SI,DI ;1st record address of OLD file = zero
MOV CX,OLD_Rec_count ;number of records on OLD file
JCXZ Compare_early_exit ;If no files in OLD directory: EXIT
MOV BP,NEW_End_Of_File ;Offset past last rec on NEW file
MOV AX,ES ;NEW file SEGment
SUB AX,File_Size_PARAs ;AX is segment address for OLD file
MOV DS,AX ;DS = SEG reg for OLD file
SUB DI,Rep_rec_len ;Point to -1 record on NEW file
SUB SI,Rep_rec_len ;Point to record -1 on OLD file
Compare_Next: ;Main compare loop - get next NEW+OLD
ADD DI,Rep_rec_len ;Advance to next NEW record
Check_Next_OLD_REC: ;Secondary compare loop: next OLD rec
ADD SI,Rep_rec_len ;Advance to next OLD rec
CMP DI,BP ;Compare offset of this new rec + EOF
JB Compare_Keys ; If we are not past EOF on this rec
CALL Displ_Deleted_Msg ; Else remaining old recs are deletd
LOOP Check_Next_OLD_REC ;Continue reporting deleted old recs
RET ;All done with main file compare
Compare_Keys:
; Compare File keys (1st 12 characters of each record)
MOV DX,CX ;Save a copy of CX (# of old recs)
MOV CX,Rep_Key_len/2 ;Compare # words in key (must be even)
PUSH DI ;Save current NEW rec loc
PUSH SI ;Save current OLD rec loc
INC SI ;Skip over file type byte
INC DI ;Skip it in NEW rec too
REPE CMPSW ;Do compare
JNE Find_Matching_NEW_Rec ;if not =, Scan NEW file for a match
; Now compare the actual records (keys already match):
; ***Warning*** the length field of following instr may be patched by /O
PATCH1: MOV CX,(Rep_NonKEY_Len)/2 ;Compare remainder of record 6 words
REPE CMPSW ;Do compare
POP SI ;Point back to start of this OLD rec
POP DI ;Point back to start of this NEW rec
JE Good_compare ;If the records match
CALL Displ_Changed_Msg ;Report detection of changed record
Good_compare:
; Now zero out the 1st byte of the matched NEW file record:
XOR AX,AX ;Zeros to store on key of record
STOSB ;Zero 1st byte
DEC DI ;Point back to start of NEW rec
MOV CX,DX ;Restore OLD file recs remaining count
LOOP Compare_Next ;Go check next record on OLD+NEW files
RET ;All done with COMPARE_FILE
;-------------------------------------------;
;Search NEW file records for a matching key ;
;-------------------------------------------;
Find_Matching_NEW_Rec: ;Search NEW file to match OLD file rec
POP SI ;Restore pointer to current record
POP DI ;Restore pointer to current record
SUB SP,2 ;Leave current NEW rec pointer on stak
Continue_NEW_File_Search:
ADD DI,Rep_rec_len ;Next record on NEW file
;Are we at End Of File (EOF) ?
CMP DI,BP ;Compare CURRENT NEW rec with EOF
JB Compare_Keys_NEW_Rec ;IF not EOF, go compare the keys
; We are at END Of File (EOF) on NEW file on this search, so report deleted rec
CALL Displ_Deleted_Msg ;Report that rec from OLD file deleted
POP DI ;point to last CURRENT rec on NEW file
MOV CX,DX ;Restore OLD file recs remaining count
LOOP Check_Next_OLD_rec ;Go check next record on OLD file
RET ;All done with COMPARE_FILE
Compare_Keys_NEW_Rec: ; (1st 13 characters of each record)
MOV CX,Rep_Key_len/2 ;Compare # words in key (must be even)
PUSH DI ;Save current NEW rec loc
PUSH SI ;Save current OLD rec loc
INC SI ;Skip over file type byte
INC DI ;Skip it in NEW rec too
REPE CMPSW ;Do compare
JE Match_rest_of_NEW_Rec ; If keys =, check the records
; Else go check next NEW rec
POP SI ;Point back to start of this OLD rec
POP DI ;Point back to start of this NEW rec
JMP SHORT Continue_NEW_File_Search ;Go try next NEW file rec
Match_rest_of_NEW_Rec: ;NEW file scan has found matching keys
; Now compare the actual records (keys already match):
; ***Warning*** the length field of following instr may be patched by /O
PATCH2: MOV CX,(Rep_NonKEY_Len)/2 ;Compare remainder of record: 6 words
REPE CMPSW ;Do compare
POP SI ;Point back to start of this OLD rec
POP DI ;Point back to start of this NEW rec
JE Found_matching_NEW_rec ;If the records match
CALL Displ_Changed_Msg ;Report detection of changed record
Found_matching_NEW_rec:
; Now zero out 1st byte of the matched NEW file record:
XOR AX,AX ;Zeros to store on key of record
STOSB ;Store zero byte at start of record
POP DI ;point to last current rec on NEW file
MOV CX,DX ;Restore OLD file recs remaining count
LOOP Check_Next_OLD_rec ;Go check next record on OLD file
RET ;All done with COMPARE_FILE
;---------------------------------------------------------------------------;
; Scan for Additions: ;
; Scan through the NEW file and look for records which have not had the ;
; 1st byte zeroed. These recs represent added files. We will put out a ;
; message recording this fact. ;
; ENTRY: ES is segment register for NEW file ;
;---------------------------------------------------------------------------;
Scan_for_Additions:
MOV AX,CS ;Used to access normal DATA segment
MOV DS,AX ;DS=CS (normal DATA segment)
CMP Changes_only,'Y' ;Does user want only changes?
JNE Scan_continue ; If not execute rest of procedure
Scan_early_Exit:
RET ; Otherwise, pack up and go home
Scan_continue:
MOV CX,NEW_Rec_Count ;Get # of 25 char recs on NEW file
JCXZ Scan_Early_Exit ;If no NEW records to scan
XOR DI,DI ;1st record on NEW file, displ = 0
SUB DI,Rep_rec_len ;Point to record # -1
MOV AX,ES ;Use both segment regs for NEW file
MOV DS,AX
Check_for_Next_Addition:
ADD DI,Rep_rec_len ;Look at next NEW file record
CMP BYTE PTR [DI],0 ;Is this a zeroed record?
JE Continue_Zero_Check ; If zeroed, keep checking
CALL Displ_added_Msg ; Else record file addition
Continue_Zero_Check:
LOOP Check_For_Next_Addition
RET
;---------------------------------------------------------------------------;
; COMPARE TOTALS - If both OLD and NEW files have total lines check that ;
; they match. If they match, yet files were changed then put out a message;
; to that effect and return ERRORLEVEL 4 at termination (BL reg). ;
; ENTRY: ES is segment register for NEW file ;
; EXIT: DS is back to original Data Segment (=CS) ;
; BL = 4 if records have changed but both totals match. ;
;---------------------------------------------------------------------------;
Compare_totals: ;Check if total===> lines match
MOV AX,CS ;Set DS back to datasegment
MOV DS,AX
MOV SI,OLD_Tot_Loc ;Point to location of totals
CMP SI,0 ;Check if totals line non-existant
JNE Continue_totals_1 ; If it exists, chk other totals line
RET ; ELSE pack up and go home
Continue_totals_1:
MOV DI,NEW_Tot_Loc ;Point to location of totals
CMP DI,0 ;Check if totals line non-existant
JNE Continue_totals_2 ; If it exists, compare the totals
RET ; ELSE pack up and go home
Continue_totals_2:
CMP BH,0FFh ;See if page mode is off (= FFh)
JE Continue_totals_3 ; IF turned off, skip page control
MOV AL,1 ; Else Set lines to be output to 2
CALL Page_Control ; + check if its time to pause
Continue_totals_3:
MOV AX,ES ;NEW file segment
SUB AX,File_Size_PARAs ;Backup segment to point to OLD file
MOV DS,AX ;DS is OLD file segment register again
MOV CX,4 ;Compare 4 bytes
REPE CMPSB ;Compare
MOV AX,CS ;Set DS back to datasegment
MOV DS,AX
MOV BP,BX ;Save highest errorlevel + line ct
MOV BX,1 ;Handle for std output device
JE Totals_Match
MOV DX, offset Tot_nomatch_Msg
MOV CX,21 ;Length of msg is 21 chars
MOV AH,40h ;DOS Write func
INT 21h ;Tell user that totals don't match
MOV BX,BP ;Restore saved BX (ERRLVL + LINEct)
RET
Totals_match:
MOV DX, offset Tot_match_Msg
MOV CX,19 ;Length of msg
MOV AH,40h ;DOS Write func
INT 21h ;Tell user file totals are equal
MOV BX,BP ;Restore saved BX (ERRLVL + LINEct)
OR BL,BL ;Check if any file chgs,dels or adds
; (IE, BL not = 0)
JE Totals_Return ; If no changes then we are done...
MOV BL,4 ; ELSE, indicate probable rename
Totals_Return:
RET
;---------------------------------------------------------------------------;
; Display CHANGED Message ;
; ;
;ENTRY: DS:SI points to OLD version of report record to format + display. ;
; ES:DI points to NEW version of report record to format + display. ;
;EXIT: ;
; Prints a message to standard output device informing user that a change ;
; has occured between the OLD and NEW files. IF /P was selected then ;
; we check line count and pause every 24 lines. ;
;---------------------------------------------------------------------------;
Displ_Changed_Msg: ;Display msg announcing changed records
PUSH BP
PUSH CX
PUSH DX
PUSH DS ;Save (OLD) file segment
MOV AX,CS ;Used to access normal DATA segment
MOV DS,AX ;DS=CS (normal DATA segment)
OR BL,BL ;Check if highest error level (BL) = 0
JNZ Check_CHG_Page_mode ; If not 0, then header msg alrdy out
CALL Print_Header_Msg ; Else Display the header message
Check_CHG_Page_mode:
CMP BH,0FFh ;See if page mode is off (= FFh)
JE Print_changed_cont ; IF turned off, skip page control
MOV AL,2 ; Else Set lines to be output to 2
CALL Page_Control ; + check if its time to pause
Print_changed_cont:
MOV BP,BX ;Save BX in BP
MOV DX, offset CHGD_Msg_1 ;First part of changed messaged
MOV CX,10 ;Length of msg is 10 chars
MOV AH,40h ;DOS Write func
MOV BX,1 ;BX=1 = Handle for std output device
INT 21h ;Write beginning of OLD file chngd msg
POP DS ;Use file segment again (OLD file)
SUB SP,2 ;Leave DS on stack
MOV DX,SI ;Point to OLD record
Call Format_and_Display_Rec ;Format compressed record for display
MOV AX,CS ;Used to access normal DATA segment
MOV DS,AX ;Back to normal data segment again
MOV DX, offset CHGD_Msg_2 ;First part of changed messaged
MOV CX,10 ;Length of msg is 10 chars
MOV AH,40h ;DOS Write func
INT 21h ;Write beginning of OLD file chngd msg
MOV AX,ES ;Use file segment again (NEW file)
MOV DS,AX
MOV DX,DI ;Point to NEW record
Call Format_and_Display_Rec ;Format compressed record for display
MOV BX,BP ;Restore Saved version of BX
POP DS ;Restore addressability to OLD file
POP DX
POP CX
POP BP
MOV BL,12 ;Indicate a record has changed
RET
;---------------------------------------------------------------------------;
; Display DELETED Message ;
; ;
;ENTRY: DS:SI points to OLD version of report record to format + display ;
; which was deleted between the OLD and the NEW report files. ;
;EXIT: ;
; Prints a message to standard output device informing user that a file ;
; has been deleted from the OLD file list. IF /P was selected then ;
; we check line count and pause every 24 lines. ;
;---------------------------------------------------------------------------;
Displ_Deleted_Msg: ;Display msg announcing deleted
PUSH BP
PUSH CX ;Save used registers
PUSH DX
PUSH DS ;Save (OLD) file segment
MOV AX,CS ;Used to access normal DATA segment
MOV DS,AX ;DS=CS (normal DATA segment)
CMP Changes_only,'Y' ;Does user want only changes?
JNE Displ_Deleted_continue ; If not continue
POP DS ; ELSE restore DS and return
JMP Displ_Deleted_Exit2 ; to caller
Displ_Deleted_continue:
OR BL,BL ;Check if highest error level (BL) = 0
JNZ Check_DEL_Page_mode ; If not 0, then header msg alrdy out
CALL Print_Header_Msg ; Else Display the header message
Check_DEL_Page_mode:
CMP BH,0FFh ;See if page mode is off (= FFh)
JE Displ_Deleted_continue2 ; IF turned off, skip page control
MOV AL,1 ; Else Set lines to be output to 2
CALL Page_Control ; + check if its time to pause
Displ_Deleted_continue2:
MOV BP,BX ;Save BX
MOV DX, offset DELETED_Msg ;First part of deleted message
MOV CX,10 ;Length of msg is 10 chars
MOV AH,40h ;DOS Write func
MOV BX,1 ;Handle for std output device
INT 21h ;Write beginning of OLD file dltd msg
POP DS ;Use file segment again (OLD file)
MOV DX,SI ;Point to OLD record
Call Format_and_Display_Rec ;Format compressed record for display
Displ_Deleted_Exit:
MOV BX,BP ;Restore saved version of BX
CMP BL,8 ;Check if Delete or chg already hapnd
JAE Displ_Deleted_Exit2 ; Do not change if already set
MOV BL,8 ;Flag that Deleted record detected
Displ_Deleted_Exit2:
POP DX
POP CX
POP BP
RET
;---------------------------------------------------------------------------;
;Display ADDED Message -Indicate that a file has been added since OLD reprt;
; ;
;ENTRY: ES:DI points to NEW version of report record to format + display. ;
;EXIT: ;
; Prints a message to standard output device informing user that a file ;
; has been added to the NEW file list. IF /P was selected then ;
; we check line count and pause every 24 lines. ;
;---------------------------------------------------------------------------;
Displ_added_Msg: ;Print message announcing additional files
PUSH BP
PUSH CX ;Save used registers
MOV AX,CS ;Used to access normal DATA segment
MOV DS,AX ;DS=CS (normal DATA segment)
OR BL,BL ;Check if highest error level (BL) = 0
JNZ Check_ADD_Page_mode ; If not 0, then header msg alrdy out
CALL Print_Header_Msg ; Else Display the header message
Check_ADD_Page_mode:
CMP BH,0FFh ;See if page mode is off (= FFh)
JE Displ_Added_continue ; IF turned off, skip page control
MOV AL,1 ; Else Set lines to be output to 2
CALL Page_Control ; + check if its time to pause
Displ_added_continue:
MOV BP,BX ;Save BX
MOV DX, offset Added_Msg ;First part of deleted message
MOV CX,10 ;Length of msg is 10 chars
MOV AH,40h ;DOS Write func
MOV BX,1 ;Handle for std output device
INT 21h ;Write beginning of file added msg
MOV AX,ES ;Use file segment again (NEW file)
MOV DS,AX ;DS=ES (both are seg reg for NEW file)
MOV DX,DI ;Point to rec on NEW file (Added rec)
Call Format_and_Display_Rec ;Format compressed record for display
MOV BX,BP ;Restore saved version of BX
POP CX
POP BP
CMP BL,8 ;Chk if Del, add or chg already hapnd
JAE AD_or_CHG_happened ; Do not change if already set
MOV BL,8 ;Flag that Deleted record detected
AD_or_CHG_happened:
RET
;---------------------------------------------------------------------------;
; Allocate Memory ;
; ;
; ENTRY: BX contains starting offset of buffer area. ;
; (Note, this area contains initialization code and data which will ;
; be overlaid once we start reading into the buffers.) ;
; ;
; Release memory used by initialization routine and allocate 2000h pages ;
; (128K) as file buffers, failing that get as much memory as possible. ;
; Leaves ES as segment pointer to first file segment (OLD file). ;
;---------------------------------------------------------------------------;
Allocate_Memory:
; Now determine how many paragraphs (16 bytes) program plus stack needs:
ADD BX,15 ;Round up to nearest PARA
MOV CL,4
SHR BX,CL ;Divide bytes of storage by 16
MOV AH,4Ah ;Dealloc all but needed (BX) paras
INT 21h
MOV BX,2000h ;Request 128K for input buffers
MOV AH,48h ;DOS request mem function
INT 21h
JNC Mem_OK ;If memory is available
MOV DX,offset Mem_loss_Msg ;Display msg informing lack of mem
MOV AH,09h ;DOS display string function
INT 21h
; Attempt to allocate what little memory is available and use that
MOV AX,BX ;Paragraphs of memory free
SHR AX,1 ;Divide by two: Space for each file
MOV File_Size_PARAs,AX ;Each file's size in pargraphs
MOV CL,4 ;Prepare to shift left 4 bits (*16)
SHL AX,CL ;Mult by 16 = number of bytes per file
MOV File_Size_Bytes,AX ;Space for each file in bytes
MOV AH,48h ;DOS alloc mem func:BX = para avail
INT 21h ;Get what memory we can get
JNC Mem_OK ;If Alloc worked 2nd time around
; Fatal memory error:
MOV DX,offset Mem_ERR_Msg ;Else: give message and give up
MOV AH,09h ;DOS display string function
INT 21h
CALL Page_Wait ;Beep and force user to hit a key
MOV AX,4C14h ; terminate with 20 error level
INT 21h
Mem_OK:
MOV ES,AX ;ES points to start of allcoated block
RET
;---------------------------------------------------------------------------;
; Read and Validate Files ;
; 1) Read both files into file buffers. ;
; 2) Validate that files are of correct type and uncorrupted:int chk data ;
; 3) Determine offset of first record and total number of records ;
; check if a total line exists on each file ;
; 4) Calc offset of beginning of last record for the NEW file ;
; ;
; On Entry ES must point to OLD_FILE segment ;
; ;
; On Exit ES will point to NEW_FILE segment and DS will be DATA SEG (=CS) ;
;---------------------------------------------------------------------------;
Read_and_Validate_Files:
; Read "OLD" file (first of two files specified)
MOV BX,OLD_File_Handle ;Get handle for the first file
PUSH OLD_Filename_end ;Save end of Filename on stack
PUSH OLD_Filename_Loc ;Save start of filename for error msgs
MOV AX,ES ;ES points to beginning of file SEG
MOV DS,AX ;DS=ES = seg reg for OLD file
CALL Read_File ;Do actual read of file + error chking
CALL Validate_File ;Check file and locate 1st + last recs
;DS = CS after return from Validate_Fi
ADD SP,4 ;Remove filename end + loc from stack
MOV OLD_Rec_count,CX ;Save number of records
MOV OLD_Tot_Loc,DI ;Save location of totals
; Read "NEW" file (2nd of two files specified)
MOV BX,NEW_File_Handle ;Get handle for the first file
PUSH NEW_Filename_end ;Save end of Filename on stack
PUSH NEW_Filename_Loc ;Save start of filename for error msgs
MOV AX,ES ;DS points to beginning of file SEG
ADD AX,File_Size_PARAs ;point DS to NEW file segment
MOV DS,AX ;DS is segment register for NEW file
CALL Read_File ;Do actual read of file + error chking
MOV AX,DS ;Validate_file needs
MOV ES,AX ; ES for file seg
CALL Validate_File ;Check file and locate 1st + last recs
;DS = CS after return from Validate_Fi
ADD SP,4 ;Remove filename end + loc from stack
MOV NEW_Rec_count,CX ;Save number of records
MOV NEW_Tot_Loc,DI ;Save location of totals (if existing)
MOV NEW_End_Of_File,DI ;Save Offset past last 25 chr rec
RET
;---------------------------------------------------------------------------;
; READ FILE - will read the file specified by the following parameters ;
; BX contains file handle, Stack contains end and start of filespec ;
; DS contains segment to read file into (file buffer is at offset zero) ;
; On EXIT: File will be read into file buffer and closed. ;
; CX will contain number of characters read in from the file. ;
;---------------------------------------------------------------------------;
Read_file:
CMP CS:Missing_Old_File,'Y' ;Are we attempting to read from a
; Non-existant OLD file?
JNE Normal_File_Read ; If not, do normal file read
XOR CX,CX ; Else, indicate file is empty
MOV CS:Missing_Old_File,0 ; Turn off missing file switch
RET ; All done for missing file
Normal_File_Read:
XOR DX,DX ;DX=0 = start of file buffer
MOV SI,DX ;SI is for BUFFER reads later
MOV CX,CS:File_Size_Bytes ;MAX # of bytes to read (Nrmly:64k-1)
MOV AH,3Fh ;Setup to read from file
INT 21h ;Call DOS to do actual read
JC Read_error ;Quit on any error or EOF
CMP AX,CX ;See if max number of characters read
JE File_size_error ;If we have compltly filled buffer
MOV CX,AX ;Save total # of chars read
MOV AH,3Eh ;Prepare to close the file
INT 21h ;Let DOS close file
RET
;---------------------------------------------------------------------------;
; READ ERROR - report read error message - call with: ;
; DI = offset to end of filename, BP= start of filename with read error ;
;---------------------------------------------------------------------------;
Read_error: ;Report error reading a file
MOV AX,CS
MOV DS,AX ;Restore datasegment addressability
MOV DX, offset Read_Err_Msg ;indicate read failed
MOV CX,15 ;Length of msg is 15 chars
MOV SI,4C14h ;DOS term with error level 20
JMP Report_file_errors
SUBTTL General Purpose subroutines
;---------------------------------------------------------------------------;
; FILE SIZE ERROR - Report on an error - with file have too many records ;
; Stack contains end and start of filespec with error ;
;---------------------------------------------------------------------------;
File_size_error: ;File has too many records for mem
MOV AX,CS
MOV DS,AX ;Restore datasegment addressability
MOV DX, offset size_Err_Msg ;indicate file size too large
MOV CX,21 ;Length of msg is 21 chars
MOV SI,4C10h ;DOS term with error level 16 (dec)
JMP Report_file_errors
;---------------------------------------------------------------------------;
; FILE TYPE ERROR - Report on an error - with file being invalid type ;
; Stack contains the END and (LOC) start of the filespec for error msgs ;
;---------------------------------------------------------------------------;
File_type_error: ;Report this is wrong type of file
MOV AX,CS
MOV DS,AX ;Restore datasegment addressability
MOV DX, offset type_Err_Msg ;indicate bad file type
MOV CX,20 ;Length of msg is 20 chars
MOV SI,4C10h ;DOS term with error level 16 (dec)
JMP Report_file_errors
;---------------------------------------------------------------------------;
; VALIDATE FILE - will examine file and locate 1st and last records in file.;
; Will also detect file corruption, by computing TOT_Rec check data and ;
; comparing with that stored in TOT_REC check data fields. ;
; On Entry: ;
; Stack contains the END and (LOC) start of the filespec for error msgs ;
; ES contains File buffer SEGMENT ;
; CX contains number of characters read in the buffer ;
; On EXIT: (if file is of valid type:) ;
; CX will be count of number of records on file ;
; DI will be = offset of CHKSUM field on final totals record ;
; this is the same as the offset of last char in last 25 char record +1;
; DS will be code/data segment register rather than File segment ;
; AX,BX,BP,DX will all be corrupted (are not saved and restored). ;
;---------------------------------------------------------------------------;
Validate_File:
MOV AX,CS ;XFER OLD data SEG(=CS)
MOV DS,AX ; back into DS
XOR BP,BP ;Start at beginning of buffer
;Handle case were file is non existant or is a 1 byte zero record file
CMP CX,REP_Rec_Len ;Do we have at least one record?
JAE File_has_Records ; Yes, do normal validation of file
CMP CX,1 ;Do we have more than 1 char on file?
JA File_Type_Error ; If so this is an invalid file
XOR CX,CX ; Else indicate this a null file
File_has_Records:
; BP now points to the first 25 char record on this file
MOV DI,CX ;Save a copy of # of chars on file
; Now check for existance of TOTALS record and locate beginning of last rec
XOR DX,DX ;Zero upper part of dividend (DX:AX)
MOV AX,CX ;# of chars after start of 1st record
MOV BX,Rep_rec_len ;Prepare to divide by record len (25)
DIV BX ;Divide chars from 1st rec by 25
; After divide: # of 25 char recs is in AX; # of chars in last rec in DX
MOV CX,AX ;# of 25 character records on file
XOR BX,BX ; Initially indicate no totals line
JCXZ Return_from_Validate ;If no records on file, we are done..
CMP DX,Tot_Rec_Len ;Length of totals/chk line should = DX
JE Chk_File_Type_Byte ; If length is correct for tot line
JMP File_type_error ; If length of final line is wrong
Chk_File_Type_Byte:
CMP ES:[BP.Rep_F_Type],5 ; IF not check 1st file type byte
JB FT_is_OK ; If 0, 1, 2, 3, or 4, its OK
JMP File_type_error ; otherwise file is corrupted
FT_is_OK:
; DI contains char count (EOF byte offset +1)
SUB DI,Tot_Rec_Len ;first char in CHKSUM field of TOT rec
CALL Detect_File_Corruption ;Verify file is OK by re-computing
; Tot_Rec check values and comparing
Return_from_validate:
RET
;-----------------------------------------------------------------------------;
; DETERMINE THAT THE REPORT FILE ITSELF HAS NOT BEEN CORRUPTED: ;
;-----------------------------------------------------------------------------;
; Calculate CHK + XOR values for the file itself and compare results with the;
; data in the last 2 bytes of the file (The 4 byte TOT_Rec). ;
; ;
; ENTRY: DI points to start of totals record (the chksum field) ;
; EXIT: Jumps to File_Type_Error if corruption is detected. ;
; ;
; AX,BX,DX,SI are all corrupted. (not saved and restored) ;
;-----------------------------------------------------------------------------;
Detect_File_Corruption:
PUSH CX ;Save critical registers
PUSH DS
MOV AX,ES ;Set DS back to file buffer
MOV DS,AX
XOR SI,SI ;Start of File buffer (offset=0)
MOV CX,DI ;OFFSET TOT_REC = # of chars bfor tot
CALL Calc_Sums ;Compute check data for report file
CMP DX,WORD PTR [DI.Tot_Int_XOR] ;Compare computed + stored CHK val
JE Check_XOR ; IF CHK values match, check XOR val
ADD SP,6 ; Remove return adr from stackand
JMP File_Type_Error ; report file as a bad file
Check_XOR:
CMP BX,WORD PTR [DI.Tot_Int_CHK] ;Compare computed + stored XOR val
JE CHK_Done ; IF XOR values match, all is OK
ADD SP,2 ; Remove return adr from stackand
JMP File_Type_Error ; report file as a bad file
CHK_Done:
POP DS ;Restore critical registers
POP CX
RET
SUBTTL General Purpose Subroutines
;******************************************************************************;
;** General purpose subroutines follow **;
;******************************************************************************;
;---------------------------------------------------;
; C A L C _ S U M S - Calculate check values ;
;---------------------------------------------------;
; This is a special version for CFCOMPC + CHKfileC. ;
;---------------------------------------------------;
; INPUT: SI = pointer to file BUFFER to scan ;
; CX = # of characters to read ;
; ;
;Register conventions: ;
; ;
; AL - Each new character read into this register ;
; CX - number of chars read in -decreasing counter ;
; BX - Contains check value 1 (checksum) ;
; DX - XOR check value 2 (XOR) for this file ;
; SI - index pointing into file BUFFER ;
; ;
; None of the above registers are saved/restored. ;
; --------------------------------------------------;
Calc_Sums:
XOR DX,DX ;Zero check value 2 (XOR hash)
XOR BX,BX ;Zero check value 1 (SUM)
XOR AH,AH ;Zero upper part of AX for addition
; Innermost char loop - keep this fast!
NEXT_CHAR:
LODSB ;Get char into AL
ROR DX,1 ;Keep shifting to XOR to right
XOR DL,AL ;cumulative XOR into DX
SUB BX,AX ;cumulative check subtraction
LOOP NEXT_CHAR ;CONTINUE SCANNING CHARS UNTIL EOB
RET ;All done calculating sums!
;---------------------------------------------------------------------------;
;Format and Display Record - Formats and displays compressed report records.;
; The compressed fields in the report records with be expanded/decrypted ;
; and formatted for display and then written to the standard output device;
; ;
;Entry: DS:DX must point to the 25 character report record to be displayed. ;
; ;
;Exit: Formatted form of report record will be displayed on STD output. ;
;---------------------------------------------------------------------------;
Format_and_Display_Rec:
PUSH BP
PUSH BX
PUSH DI
PUSH DS
PUSH ES
PUSH SI
MOV AX,CS ;Used to access normal DATA segment
MOV ES,AX ;ES allows us to addr in data segment
ASSUME DS:Nothing, ES:CSEG ;Tell ASM to use ES to addr norm data
;-----------------;
; Format file name;
MOV CX,12 ;Scan 12 characters of filename
MOV DI,OFFSET DSP_Rec ;Start of DSP_REC - file for display
MOV SI,DX ;Start of Rep_Rec (compressed report)
MOV BP,DX ;Save the start of the record
INC SI ;Skip to start of filename field
Xfer_file_name: ;Xfer filename from Rep_Rec to DSP_REC
LODSB ;Load 1 byte from Rep_Rec for transfr
OR AL,AL ;See if this=0 (end of file name)
JZ Blank_fill ;If end, then blank fill rest of name
ROR AL,1 ;Decrypt the filename
STOSB ;Else store char in OutRec file name
LOOP Xfer_file_name ;continue until done
JMP Short Check_File_Type ;Go and format file size for output
Blank_fill: ;blank fill remainder of file name out
MOV AL,' '
REP STOSB ;Store remaining characters
Check_File_Type: ;What file type is this?
MOV AL,BYTE PTR DS:[BP] ;Pick up the file type byte
CMP AL,1 ;Is this a normal file (=1) ?
JE Cnvt_CHKvals ; Its a file, so do normal processing
CMP AL,2 ;Is this a directory entry ?
JE Process_directory ; Its a directory
CMP AL,3 ;Is it a file with open error?
JE Process_File_with_OPNerr ; Its a file CHKfileC couldn't open
CMP AL,4 ;Is it a file that had an I/O error?
JE Process_File_with_IOerr ; Its a file CHKfileC couldn't read
Process_directory:
; Do special handling for directory entries (rather than file entries):
MOV DI,offset DSP_CHK_Sum ;prep to place msg in chk sum field
MOV AX,'iD' ;Store "Dir." in display record
STOSW ; to indicate this is a directory
MOV AX,'.r' ; entry rather than a normal file.
STOSW
MOV CX,5 ;Now blank fill the remainder of
MOV AL,' ' ; the report record chk value 2
REP STOSB
JMP SHORT Format_F_Size ;Do not format check values (1+2)
Process_file_with_OPNerr: ;This is a file CHKfileC couldn't open
MOV SI,OFFSET File_Open_Err_Msg ;Data to replace check values
Process_bad_files: ;Come here to displ MSGs (adr in SI)
PUSH DS ;Save DS (adr into file segment)
MOV AX,CS
MOV DS,AX ;DS must adr data segment for MOVSB
MOV DI,OFFSET DSP_CHK_Sum ;Field to indicate
MOV CX,9 ;Message has 9 chars
REP MOVSB
POP DS ;Restore addressing to file segment
JMP SHORT Format_F_Size ;Go display rest of file info
Process_file_with_IOerr: ;This is a file CHKfileC couldn't read
MOV SI,OFFSET File_Read_Err_Msg ;File IO error msg to put in DSP rec
JMP SHORT Process_Bad_Files ;Go put message in DSP_REC
Cnvt_CHKvals: ;Convert Check values for display
MOV SI,BP ;Point back to the start of the rec
MOV BX,[SI.REP_CHK_SUM] ;Get CHKsum (check val 1) in BX
MOV CX,[SI.REP_XOR_SUM] ;GET XORsum (check val 2) in CX
CALL Convert_Sums_for_Display ;Convert to display form and place in
; the the display record (DSP_REC)
; --------------------------------------------------;
; Extract file size from Rep_Rec for display ;
; --------------------------------------------------;
Format_F_size: ;Format Rep_Rec's file size for output
MOV DI,offset DSP_F_Size ;Display formatted file size
LEA SI,[BP+Rep_FS_HIr+1] ;End of Rep_Rec file size area
MOV CX,4 ;process 4 bytes (2 words) file size
XOR DX,DX ;DX=0 means only leading zeros so far
Get_byte_to_Hex_Convert: ;Conv each byte to 2 HEX ASCII digits
MOV AH,BYTE PTR [SI] ;Pick up last chr from Rep_Rec F size
CALL Convert_Hex_ASCII ;Convert 4 bit hex to ASCII displcode
DEC SI ;Get prior byte in Rep_Rec file size
LOOP Get_Byte_to_Hex_Convert ;Do all four bytes
; --------------------------------------------------;
; Extract file date from Rep_Rec for display ;
; --------------------------------------------------;
MOV AX,DS:[BP.Rep_F_date] ;Date of last file update
; date is in yyyyyyym mmmddddd format (year is offset from 1980)
MOV DX,AX ;Save a copy of file date
MOV BL,10 ;Put 10 in BL for decimal conversion
;MONTH
MOV CL,5 ;Prepare to shift right 5 bits
ROR AX,CL ;Move month to right of word
AND AX,0Fh ;only month remains
MOV DI,offset DSP_F_MM ;Point to month field in DSP_rec
Call Convert_Dec_ASCII ;Convert 2 decimal digits to ASCII
;DAY
MOV AX,DX ;Restore copy of Rep_Rec date
AND AX,1fh ;Extract day portion of date
MOV DI,OFFSET DSP_F_DD ;Point to day field in DSP rec
Call Convert_Dec_ASCII ;Convert 2 decimal digits to ASCII
;YEAR
MOV AX,DX ;Restore copy of Rep_Rec date
MOV CL,7
ROL AX,CL ;Bring year of date to right of AX
AND AX,7Fh ;MASK off right 7 bits of YEAR
ADD AX,80 ;Year is years after 1980
CMP AX,100 ;Are we in the next century?
JB Transfer_year ;IF not go ahead and display YY
SUB AX,100 ;Otherwise adjust the year
Transfer_year:
MOV DI,OFFSET DSP_F_YY ;Point to year field in DSP_REC
Call Convert_Dec_ASCII ;Convert 2 decimal digits to ASCII
; --------------------------------------------------;
; Extract file time from Rep_Rec for display ;
; --------------------------------------------------;
MOV AX,DS:[BP.Rep_F_time] ;time of last file update
; time is in hhhhhmmm mmmsssss format (seconds are 0-29 in 2 sec intervl)
;HOURS
MOV DX,AX ;Create a copy of the time
MOV CL,5 ;Shift for hour bits
ROL AX,CL ;hours are on the right of AX
AND AX,1Fh ;Only hours remain in AX
MOV DI,OFFSET DSP_F_HH ;Point so we can mov to hour field
Call Convert_Dec_ASCII ;Convert 2 decimal digits to ASCII
;Minutes
MOV AX,DX ;Restore copy of the time
MOV CL,5
ROR AX,CL ;Minutes are rightmost in AX
AND AX,3Fh ;Mask off minutes
MOV DI,OFFSET DSP_F_MI ;Point to minutes in display field
Call Convert_Dec_ASCII ;Convert 2 decimal digits to ASCII
;Seconds
MOV AX,DX ;Restore copy of the time
AND AX,1Fh ;Only seconds remain after MASKing
ROL AX,1 ;Multiply secs by 2
MOV DI,OFFSET DSP_F_SS ;Point so we can mov to hour field
Call Convert_Dec_ASCII ;Convert 2 decimal digits to ASCII
ASSUME DS:CSEG, ES:Nothing ;Return ASM to using DS for norm data
; Do actual display of DISPLAY (DSP_REC) record:
MOV AX,CS ;Used to access normal DATA segment
MOV DS,AX ;DS allows DOS to access to DSP_REC
MOV BX,1 ;File handle for STD output
MOV DX, OFFSET DSP_Rec ;Location of record to display
MOV CX,DSP_Rec_len ;Length of msg is 51 chars
MOV AH,40h ;DOS Write func
INT 21h ;Write actual OLD report rec out
POP SI
POP ES
POP DS
POP DI
POP BX
POP BP
RET
;------------------------------------------------------------------------------;
; Convert 16 bit check values to ASCII hex characters and store in DSP_Rec ;
;------------------------------------------------------------------------------;
;Input: BX contains checksum (16 bit) (Check value # 1) ;
; CX contains 16 exclusive or sum (XOR, Check value # 2) ;
;Output: DSP_Rec will contain display versions of chk and XOR sums ;
; AX,DX and DI will be corrupted. ;
;------------------------------------------------------------------------------;
Convert_Sums_for_Display: ;Come here only for files not DIRs
; chksum (val1) in BX, XOR(val2) in CX
MOV DI,OFFSET DSP_CHK_Sum ;Field for hex ASCII characters
MOV AH,BH ;Upper byte of checksum (chk value 1)
MOV DL,01 ;DX<>0 turns off leading 0 suppression
CALL Convert_Hex_ASCII ;Convert to 2 hex ASCII characters
MOV AH,BL ;Lower byte of checksum (chk val 1)
CALL Convert_Hex_ASCII ;Convert to 2 hex ASCII characters
INC DI ;Skip over space + into XOR field
MOV AH,CH ;Upper byte of XOR (chk value 2)
CALL Convert_Hex_ASCII ;Convert to 2 hex ASCII characters
MOV AH,CL ;lower byte of XOR (chk value 2)
CALL Convert_Hex_ASCII ;Convert to 2 hex ASCII characters
RET
;------------------------------------------------------------------------------;
; CONVERT HEX ASCII - Convert hex byte (8 bits) to 2 ASCII display bytes ;
;------------------------------------------------------------------------------;
Convert_Hex_ASCII:
CALL Convert_Hex_Nyble_ASCII ;Convert nyble (4 bits) to hex ASCII
CALL Convert_Hex_Nyble_ASCII ;Convert nyble (4 bits) to hex ASCII
RET ;RETURN to caller
;------------------------------------------------------------------------------;
; CONVERT HEX NYBLE ASCII - Convert hex digit (4 bits) to 1 ASCII display byte ;
;------------------------------------------------------------------------------;
;Input: AH upper 4 bits contain hex to be converted. DI points to output record;
; DX should be set to zero for leading zero suppression. ;
;Output: 1 byte in output record will contain ASCII display code of HEX byte. ;
; AH be shifted 4 bits to left. AL will contain ASCII version of HEX. ;
; DI will be incremented twice. DX will be = SP if nonzero output ;
;------------------------------------------------------------------------------;
Convert_Hex_Nyble_ASCII:
XOR AL,AL ;Zero out AL
ROL AX,1 ;Move half byte (hex digit) into AL
ROL AX,1
ROL AX,1
ROL AX,1 ;AL now contains a hex digit
OR AL,AL ;Is this a zero?
JE Leading_Zero_check ;If so check if this is leading zero
MOV DX,SP ;Set non 0 to indicate non 0 found
Continue_hex_check:
CMP AL,9 ;Is digit 0 to 9 or A to F ?
JA Hex_alpha_digit ;If hex A to F, do special conversion
ADD AL,'0' ;Convert hex digit to ASCII display
STOSB ;store in display field (Out_F_size)
RET
Hex_alpha_digit: ;Convert hex A to F to ASCII display
ADD AL,'A'-10
STOSB ;store in display field (Out_F_size)
RET
Leading_Zero_Check: ;Check and blank out leading zeros
OR DX,DX ;Have non zeros been detected yet?
JNZ Continue_hex_check ;If so then do normal processing
MOV AL,' ' ;Else put out a leading blank
STOSB
RET
;------------------------------------------------------------------------------;
; CONVERT DEC ASCII - Convert 2 decimal digits to 2 ASCII display bytes ;
;------------------------------------------------------------------------------;
;Input: BL contains 10. AX contains decimal number 0 to 99 to be converted. ;
; DI points to destination to store result ;
;Output: AH will have lower decimal ASCII digit and AL uppper digit. ;
; Both digits will be stored at location pointed to by DI. ;
;------------------------------------------------------------------------------;
Convert_DEC_ASCII:
DIV BL ;Divide by 10
ADD AX,'00' ;Convert to ASCII digits
STOSW
RET
;---------------------------------------------------------------------------;
; Report File errors - General purpose error presenter used by ;
; the specific error subroutines such as file_size_error + read_error. ;
; Stack contains the END and (LOC) start of the filespec where error was ;
; DX = offset to error msg, CX has length of message ;
; SI = contains DOS terminate code with specific ERRORLEVEL in lower part ;
;---------------------------------------------------------------------------;
Report_File_errors: ;General purpose error display routine
;DOS term func + errlvl must be in SI
MOV AX,CS ;Point ES back
MOV ES,AX ; to normal data segment
MOV AH,40h ;DOS Write func
MOV BX,1 ;Handle for std output device
INT 21h ;Write beginning of open error message
ADD SP,2 ;Remove return address from stack
POP BP ;Get starting offset of filespec
POP DI ;1 character after end of filespec
MOV AX,CRLF ;Terminate file name with CR,LF
STOSW
SUB DI,BP ;Calc filename length + 2 for CRLF
MOV CX,DI
MOV DX,BP ;Start of file name to output
MOV AH,40h ;DOS Write func
INT 21h
CALL Page_Wait ;Beep and force user to hit a key
MOV AX,SI ;SI contains 4Ch with error lvl
INT 21h
;---------------------------------------------------------------------------;
; PAGE CONTROL: (and *PAGE WAIT* alternate entry point) ;
; Called only if user specified /P option (BH will be not = to FFh). ;
; Page control will increment the line counter and pause every 24 lines ;
; giving the user a prompt to -Hit any KEY - ;
;ENTRY: ;
; AL contains number of lines waiting to be displayed. ;
; BH contains line count for this page already. ;
; DS points to normal data segment (=CS) ;
;EXIT: ;
; BH contains updated line count which is reset if page wait happened ;
;---------------------------------------------------------------------------;
;PAGE WAIT - Alternate entry point ;
; - simply puts out -HIT ANY KEY- message and waits for user ;
; to hit any key. ;
;---------------------------------------------------------------------------;
Page_control:
ADD BH,AL ;Increment line counter
CMP BH,24 ;Are we over one page of output?
JA Page_Wait ; If over 1 page then do page wait
RET
Page_Wait: ;Alternate entry point (here)
MOV BH,AL ;Reset the line counter
PUSH BP ;Save all corrupted registers
PUSH BX
PUSH CX
PUSH DI
PUSH DX
PUSH SI
; Produce a beep to alert the user: (use BIOS TTY func to write an ASCII BELL)
MOV AX,0E07h ;BIOS func (0Eh) to write (07H) beep
XOR BH,BH ;Select page zero for output
INT 10h ;BIOS video function (0Eh=write char)
;Find out what attribute is being used for display
MOV AH,08h ;read attrib + char function
INT 10h ;Call BIOS
PUSH AX ;Save AH=attribute byte
;Find out what line the cursor is on
MOV AH,03h ;Read cursor position function
INT 10h ;BIOS video services
PUSH DX ;DH contains row (line #) Save it!
; Position cursor to current line + column 28: (TO BIOS row 27)
MOV AH,02 ;BIOS int 10h set cursor position func
XOR BH,BH ;Set page to zero
;DH contains current row
MOV DL,1Bh ;Set cursor current row and col 27
INT 10h ;BIOS video services
; Put -Hit any key- message out with inverse video attribute type on
; XOR BH,BH ;Set page to zero (BH is still 0)
MOV BL,0F0h ;Inverse video attribute
MOV CX,1 ;Character count
MOV SI,offset Hit_Key_Msg ;The hit-any-key message
Display_next_video_char:
MOV AH,09h ;BIOS int 10h write attrib + char func
LODSB ;Get next character for output
PUSH SI ;Save SI (int 10h may corrupt it)
INT 10h ;Put character and attribute out
INC DX ;Advance cursor position
MOV AH,02 ;Adv cursor function
INT 10h ; advance the cursor (BIOS)
POP SI ;Restore saved SI
CMP SI,offset Hit_key_Msg_end ;are we at end of message?
JB Display_next_video_char ; If not get next char for display
; Else, wait for key press by user
; Wait for user to hit any key
XOR AX,AX
INT 16h ;Wait for user to hit a key
; Erase HIT ANY KEY message
POP DX ;DH=current line number
POP BX ;BH=user's screen attribute
MOV AH,06h ;INIT window function
XOR AL,AL ;Zero AL to clear window
MOV CH,DH ;Current row (y coor upr lft)
MOV CL,00 ;Start in first char position
MOV DL,79 ;Last char pos - blank entire line
INT 10h ;Blank out line
; Position cursor to start of blanked line
MOV AH,02 ;BIOS int 10h set cursor position func
XOR DL,DL ;DH=cur line, DL=0: first char pos
XOR BX,BX ;Use video page zero
INT 10h ;BIOS video services
POP SI ;Restore all corrupted registers
POP DX
POP DI
POP CX
POP BX
POP BP
RET ;Return to caller
;---------------------------------------------------------------------------;
; PRINT HEADER Msg - Displays column headers the first time CFCOMPC decides ;
; it needs to display a changed record. ;
;ENTRY: ;
; DS points to normal data segment (=CS) ;
;EXIT: ;
; DX,CX,AX are corrupted. ;
;---------------------------------------------------------------------------;
Print_Header_Msg:
PUSH BX ;Save BX
MOV BX,1 ;Write to STD output device (=1)
MOV DX, offset Header_Msg ;beginning loc of directory string
MOV CX,179 ;182 chars in header message
MOV AH,40h ;DOS write
INT 21h
POP BX
RET
SUBTTL Definition of Data structures
PAGE
;******************************************************************************;
;** Definition of Data areas follow **;
;******************************************************************************;
File_Size_PARAs DW 1000h ;Size of each REP file in paragraphs
File_Size_Bytes DW 0FFFFh ;Size of each REP file in bytes
OLD_Filename_Loc DW 0 ;offset of filespec for OLD file
OLD_Filename_end DW 0 ;end of filespec for OLD report file
OLD_File_Handle DW 0 ;File handle for OLD report file
OLD_Rec_count DW 0 ;Number of 25 char report recs on file
OLD_Tot_Loc DW 0 ;Offset of CHKSUM on totals records
NEW_Filename_Loc DW 0 ;offset of filespec for NEW file
NEW_Filename_end DW 0 ;end of filespec for NEW report file
NEW_File_Handle DW 0 ;File handle for NEW report file
NEW_Rec_count DW 0 ;Number of 25 char report recs on file
NEW_Tot_Loc DW 0 ;Offset of CHKSUM on totals records
NEW_End_Of_File DW 0 ;Offset 1st chr past last 25 chr rec
Changes_Only DB 0 ;="Y" if user wants only changes
Missing_Old_File DB 0 ;="Y" if OLD report file not found
Page_Mode DB 0FFh ;=00h means stop after each page
File_Open_Err_Msg DB 'Open fail'
File_Read_Err_Msg DB 'Read fail'
Mem_Err_Msg DB 'Error in MEM ALLOC'
CRLF_Msg DB CR,LF,'$'
Mem_loss_Msg DB 'Memory lack limits file size'
DB CR,LF,'$'
Read_Err_Msg DB 'Error reading: '
Type_Err_Msg DB 'Wrong type of file: '
DB CR,LF
Size_Err_Msg DB 'File size too large: '
CHGD_Msg_1 DB 'Chgd: OLD:'
CHGD_Msg_2 DB ' NEW:'
Deleted_Msg DB 'Deleted-->'
Added_Msg DB 'NEW Fil-->'
Tot_match_Msg DB 'File totals match',CR,LF
Tot_nomatch_Msg DB 'File totals unequal',CR,LF
Hit_Key_Msg DB '-Hit any key-'
Hit_Key_Msg_end EQU $
Header_Msg DB ' File Name + Check Check File Update Update'
DB CR,LF
DB ' Extension: Val1: Val2: Size: Date: Time:'
DB CR,LF
DB ' ---------- ---- ---- ----- ------ ------'
DB CR,LF
;******************************************************************************;
;** Definition of DSP (Display) file record: **;
;** This is the record description of individual check records for output. **;
;** These records are expanded versions read from the new + old report files **;
;******************************************************************************;
DSP_Rec EQU $ ;Name for the entire display record
DSP_F_Name DB 12 DUP (' ') ;12 spaces reserved for filename
DB ' '
DSP_CHK_Sum DB 4 DUP ('0') ;Check value 1: 4 hex digits
DB ' '
DSP_XOR_Sum DB 4 DUP ('0') ;Check value 2: 4 hex digits
DB ' '
DSP_F_Size DB 8 DUP ('0') ;Size of file: 8 Hex digits
DB ' '
DSP_F_MM DB 'MM' ;Date of last file update:
DB '/'
DSP_F_DD DB 'DD'
DB '/'
DSP_F_YY DB 'YY'
DB ' '
DSP_F_HH DB 'HH' ;Time of last file update
DB ':'
DSP_F_MI DB 'MI' ;MInutes
DB ':'
DSP_F_SS DB 'SS'
DB CR,LF ;End of DSP_Rec (display record) desc
SUBTTL INIT data + code (also input BUFFERs + stack)
;******************************************************************************;
;** Definition of file buffer Data areas and code follow: **;
;** All the following storage will be overlaid when records are read in **;
;******************************************************************************;
EVEN
Buffer_area label byte ;All storage + code following is in
; the input file buffer.
; ----------------------------------------------------------------------------;
; Initialization code - parse parms + put out msgs and open both files ;
; ----------------------------------------------------------------------------;
Parse_Parms_Open_Files: ;Parse input parameters + displ header
MOV SI,80h ;Parameter area in PSP
MOV CL,[SI] ;Get # of chars in input parm
XOR CH,CH ;Clear upper byte of char count
INC SI ;Point to first char
;---------------------------------------------------------------------------;
; Conventions for command line parsing: ;
; SI points to next char to be checked in the parm field at DS:80 ;
; CX is count of characters left to be scanned ;
; BP points to start of current processed filespec. ;
;---------------------------------------------------------------------------;
Call Parse_Filespec ;exract 1st filespec from parm area
CMP AL,'/' ;Is the file separator char a "/"?
JNE Filespec1_OK ; If its not an early parm ("/")
JMP No_File_Exit ; Display error + correct syntax
Filespec1_OK:
MOV OLD_Filename_Loc,BP ;Store location of file name
MOV OLD_Filename_end,DI ;Store char loc after end of filespec
; Adjust CX to reflect actual characters left to be scanned
JCXZ Skip_decrement ;Don't decrement CX if already = 0
DEC CX
Skip_decrement:
Call Parse_Filespec ;extract 2nd filespec from parm area
MOV NEW_Filename_Loc,BP ;Store location of 2nd file name
MOV NEW_Filename_end,DI ;Store char loc after end of filespec
CALL Parse_parms ;Process any "/" parms
CALL Put_Out_Initial_MSGs ;Display start messages
MOV DX,OLD_Filename_Loc ;Open the first ("OLD") file
MOV AX,3D00h ;DOS open file (handle) for read cmnd
INT 21h ;invoke DOS
JNC Continue_Open ;If no errors continue processing
; If open fails for OLD file, treat it as if file were empty - keep going
MOV Missing_Old_File,'Y' ;Else, Indicate old file was gone
MOV DI,OLD_Filename_end
CALL File_Open_Error ;Put out file open error message
MOV DX, OFFSET Empty_OLD_Msg ;Tell user we will pretend file is MT
MOV AH,09h ;DOS display string function
INT 21h
Continue_Open:
MOV OLD_File_handle,AX ;Save DOS file handle
MOV DX,NEW_Filename_Loc ;Open the 2nd (AKA "NEW") file
MOV AX,3D00h ;DOS open file (handle) for read cmnd
INT 21h ;invoke DOS
JNC Open_done ;If no errors continue processing
MOV DI,NEW_Filename_end
CALL File_Open_Error ;Put out file open error message
CALL Page_Wait ;Beep and force user to hit a key
MOV AX,4C14h ; terminate with 20 error level
INT 21h
Open_done:
MOV NEW_File_handle,AX ;Save DOS file handle
RET
;---------------------------------------------------------------------------;
; Parse filespec: ;
; Input: ;
; SI points to next char to be checked in the parm field at DS:80 ;
; CX is count of characters left to be scanned ;
; ;
; Returns: ;
; AL contains final separator found at end of filespec (if any). ;
; BP points to start of filespec ;
; DI points to byte after last char in filespec ;
; Filespec is zero terminated in the parameter area ;
;---------------------------------------------------------------------------;
Parse_Filespec: ;Extract and zero terminate filename
OR CL,CL ;Check for 0 chars (NO INPUT)
JZ No_File_Exit ;If no parms, put out error msg
DEL_SPACES: ;Get rid of extra spaces
LODSB ;Get byte at DS:SI and inc SI
CMP AL,' ' ;Is it a space?
JNE Set_File_name ;If not, we should have a file name..
LOOP DEL_SPACES ;Cont checking unitl last char
No_File_Exit: ;For no input, explain syntax to user:
MOV DX, OFFSET NO_FILE_Msg ;Prepare error message
MOV AH,09h ;DOS display string function
INT 21h
JMP SHORT Give_Syntax_and_Quit ;Give user correct syntax + Termn
;--------------------------------------------;
; Parse file spec and zero byte terminate it ;
;--------------------------------------------;
Set_File_Name:
DEC SI ;point back to 1ST letter of filespec
MOV BP,SI ;Save a copy of filespec start
Scan_To_File_Spec_End:
; start scanning the file specification and transfer into output field
LODSB ;Get next char of file spec
CMP AL,' ' ;check valid separator character
JBE file_spec_end_found
CMP AL,'/' ;check for valid separator
JE file_spec_end_found
CMP AL,',' ;check for valid separator
JE file_spec_end_found
LOOP Scan_To_File_Spec_End
INC SI ;Adjust SI if no separator char found
File_Spec_End_Found:
; SI is pointing 2 characters past end of filespec at this time
MOV DI,SI
DEC DI ;DI points to 1st char after filespec
MOV BYTE PTR [DI],00 ;zero terminate the filespec: ASCIIZ
RET
;----------------------------------------------------------------;
; Parse Parms: parse /P and /C parameters ;
; Input: SI must point to next character to process ;
; CX contains # of chars left in paramter area ;
;----------------------------------------------------------------;
Parse_Parms:
Check_parm_chars_left: ;Check if enough chars left for a parm
CMP CX,01 ;Check if no more chars to scan
JA Parm_Scan ; If Not, continue checking
RET ; If no more chars, we are done
Parm_Scan: ;Check for presence of a /_ parm
CMP AL,'/' ;check for "/" parm character
JE Parm_found
CMP AL,' ' ;Check for blanks
JNE Unrecog_parm ;If other than blank its illegal...
LODSB ;Keep checking next character
LOOP Parm_Scan
RET ;Finished (parsing parms)
Parm_Found: ;Check if parm is valid
DEC CX ;Adjust chars remaining counter
JCXZ Unrecog_parm ;IF no chars left then parm is invalid
LODSB ;Get next char
DEC CX ;Adjust chars remaining counter
AND AL,5Fh ;Capitalize char
CMP AL,'P' ;Is it the "Totals wanted" parm?
JE P_parm ;T parameter detected
CMP AL,'C' ;Is it alternate Check Sum parm?
JE C_parm ;C parameter detected..
CMP AL,'O' ;Is it "Only chk field compare parm"?
JE O_parm ;C parameter detected..
Unrecog_parm: ; an illegal parameter:
MOV DX, offset Bad_Parm_Msg ;indicate illegal parm was found
MOV AH,09h ;DOS display string function
INT 21h
Give_Syntax_and_Quit:
CALL Page_Wait ;Beep and force user to hit a key
MOV DX, offset Syntax_Msg ;Give user the correct syntax
MOV AH,09h ;DOS display string function
INT 21h
MOV AX,4C80h ; terminate with 128 error level
INT 21h
P_parm:
MOV Page_Mode,03h ;Indicate user wants page mode
;Std out already has 3 line header
;Originally =FFh to turn page mode off
LODSB ;Keep checking next character
JMP SHORT Check_Parm_chars_left ;Check for additional parms
C_parm:
MOV Changes_Only,'Y' ;User wants only changes (no add/del)
LODSB ;Keep checking next character
JMP SHORT Check_Parm_chars_left ;Check for additional parms
O_parm: ;Compare only Check fields
; IF the /O option is selected, we will patch the length field of two
; MOV CX,06 instructions in the COMPARE_FILES subroutine labeled PATCH1 and
; PATCH2. These instructions will then only compare 4 words (the CHECK fields
; and file size) rather than the entire remaining record.
MOV BYTE PTR[PATCH1+1],04 ;Patch length field of MOV CX instr
MOV BYTE PTR[PATCH2+1],04 ;Patch length field of MOV CX instr
LODSB ;Keep checking next character
JMP SHORT Check_Parm_chars_left ;Check for additional parms
;----------------------------------------------------------------;
; File Open Error - put out file open error message + terminate;
; Input: DX must point to start of filespec ;
; DI must point to end of filespec ;
;----------------------------------------------------------------;
File_Open_Error:
PUSH DX ;Save filename
MOV DX, offset Open_Err_Msg ;Indicate open failed
MOV CX,24 ;Length of msg is 24 chars
MOV AH,40h ;DOS Write func
MOV BX,1 ;Handle for std output device
INT 21h ;Write beginning of open error message
POP DX ;restore name of file loc to DX
SUB DI,DX ;Calc length of filename
MOV CX,DI ;CX contains file length
MOV AH,40h ;DOS Write func
INT 21h
MOV DX,OFFSET CRLF_Msg ;Put out a carriage return line-feed
MOV AH,09h ;DOS display string func
INT 21h
RET
Put_Out_Initial_MSGs: ;Display header and start messages
MOV DX, offset Start_Msg ;beginning of start message
MOV CX,SM_end-Start_MSG ;Start message begins with 61 chars
MOV AH,40h ;DOS Write func
MOV BX,1 ;Handle for std output device
INT 21h ;Write Start message
MOV SI,offset Start_Dir ;Place to store current directory
XOR DL,DL ;Zero DL in order to use default drive
MOV AH,47h ;Get current directory (path) func
INT 21h
CLD ;Scan in forward direction
MOV DI,offset Start_Dir ;Scan dir strng to determine length
XOR AX,AX ;Scan for zero termination of dir
MOV CX,64 ;Scan up to 64 chars of directory
REPNE SCASB ;Find 1st zero byte
MOV AX,CRLF
STOSW ;Terminate dir string with CR LF
MOV DX, offset Start_Dir ;Beginning loc of directory string
SUB DI,SI ;Calc length of directory string
MOV CX,DI ;Length reg for DOS write function
MOV AH,40h ;DOS Write func
INT 21h ;Write Dir string to finish start msg
RET
; --------------------------------------------------;
; Initialization DATA STORAGE ;
; --------------------------------------------------;
Start_MSG DB CR,LF,"CFCOMPC 1.0 ",BOX," PCDATA TOOLKIT (c) 1990"
DB " Ziff Communications Co.",CR,LF
DB "PC Magazine ",BOX," Wolfgang Stiller - In directory: \"
SM_End LABEL BYTE ;End of the Start message
Start_Dir DB 66 DUP (0)
Open_ERR_Msg DB 'CFCOMPC unable to open: '
Empty_Old_Msg DB 'OLDfile assumed empty - execution continues.',CR,LF,'$'
Bad_Parm_Msg DB 'Unrecognized parameter detected.',CR,LF,LF,'$'
NO_FILE_Msg DB 'You must specify at least OLD and NEW file names to compare.'
DB CR,LF,'$'
Syntax_Msg DB "CFCOMPC 1.0 ",BOX," PCDATA TOOLKIT Copyright (c) 1990"
DB " Ziff Communications Co.",CR,LF
DB "PC Magazine ",BOX," Wolfgang Stiller",CR,LF
DB CR,LF,'CFCOMPC does a high speed compare of the compressed'
DB ' report files produced',CR,LF
DB 'by CHKfileC. It displays all changes between the OLD and '
DB 'NEW report files.',CR,LF,LF
DB 'Syntax is: CFCOMPC OLDfile NEWfile [/C] [/O] [/P]'
DB CR,LF,LF
DB ' OLDfile and NEWfile are files created by CHKFILEC.COM.'
DB CR,LF,LF
DB ' "/C" Display only changed files not additions or '
DB 'deletions.',CR,LF
DB ' "/O" Only use check fields and file size in comparing '
DB 'files.'
DB CR,LF
DB ' DOS time and Date stamps are not used for compare.'
DB CR,LF
DB ' "/P" Pause between pages if changes found.'
DB CR,LF,'$'
CSEG EndS
END CFCOMPC